package com.myown.jdk.thread;

import java.util.ArrayList;
import java.util.List;

/**
 * 1. 圆桌上有五位哲学家，每两人中间有一个筷子。 <p>
 * 2. 每个哲学家有两件事情要做： <p>
 * &nbsp&nbsp 1)思考 <p>
 * &nbsp&nbsp 2)吃饭。哲学家必须同时拿到两只筷子，才能吃饭。 <p>
 * 3. 哲学家之间并不知道对方何时要吃饭，何时要思考，不能协商制定分时策略。 <p>
 * 4. 设计一个拿筷子的策略，使得哲学家之间不会因为拿筷子而出现死锁或者活锁。 <p>
 *
 * @author lincky
 * @version v1.0.0
 * @time 2017.03.22 10:41
 */
public class DeadLock {



    static class ChopStick {
        volatile boolean available;// 可用

        volatile boolean taken;

        public ChopStick() {
            this.available = true;
        }

        synchronized void take() {
            while (!available) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            available = false;
        }

        synchronized void drop() {
            available = true;
            notify();
        }

        @Override
        public String toString() {
            return "ChopStick{" +
                    "available=" + available +
                    '}';
        }
    }

    static class Philosopher {
        static int count;
        final int id;
        boolean print;

        final ChopStick leftStick;
        final ChopStick rightStick;

        public Philosopher(ChopStick leftStick, ChopStick rightStick) {
            this.id = ++count;
            this.leftStick = leftStick;
            this.rightStick = rightStick;
        }

        void print() {
            print = true;
        }

        void print(String msg) {
            if (print) {
                System.out.println(msg);
            }
        }

        private static final long DINNER_TIME = 1000;// 1s

        private static final long THINKING_TIME = 2 * 1000;// 2s

        void think(String extral) {
            print(this + " is thinking " + (extral != null ? extral : ""));
            try {
                Thread.sleep(THINKING_TIME);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        void think() {
            think("");
        }

        void eat() {
            while (!leftStick.available) {
                think("waiting leftStick");
            }
            leftStick.take();
            while (!rightStick.available) {
                think("waiting rightStick");
            }
            rightStick.take();
            print(this + " is eating");
            try {
                Thread.sleep(DINNER_TIME);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            leftStick.drop();
            rightStick.drop();
        }

        @Override
        public String toString() {
            return "Philosopher{" +
                    "id='" + id + '\'' +
                    ", leftStick=" + leftStick +
                    ", rightStick=" + rightStick +
                    '}';
        }
    }

    static class WorkThread implements Runnable {

        Philosopher philosopher;

        public WorkThread(Philosopher philosopher) {
            this.philosopher = philosopher;
        }

        @Override
        public void run() {
            while (!Thread.interrupted()) {
                philosopher.think();
                philosopher.eat();
            }
        }
    }

    public static void main(String[] args) {
        int size = 5;
        List<ChopStick> chopSticks = new ArrayList<>();
        List<Philosopher> philosophers = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            chopSticks.add(new ChopStick());
        }
        for (int i = 0; i < size; i++) {
            if (i == size - 1) {
                philosophers.add(new Philosopher(chopSticks.get(0), chopSticks.get(i)));
                break;
            }
            philosophers.add(new Philosopher(chopSticks.get(i), chopSticks.get(i + 1)));
        }
        philosophers.get(0).print();
        for (int i = 0; i < size; i++) {
            new Thread(new WorkThread(philosophers.get(i)), "Thread" + i).start();
        }
    }

}
